﻿using Freebase4net.Interfaces;
using Freebase4net.Rest;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
using Newtonsoft.Json.Linq;

namespace Freebase4net
{
    public class MqlReadService : BaseService<MqlReadServiceResponse>
    {
        public MqlReadService(string apiKey)
            : base(apiKey)
        {
        }

        /// <summary>
        /// Run all passed queries in async and returns list of tasks
        /// </summary>
        /// <param name="queries">Array of queries that must be executed in async way</param>
        /// <returns>Object that contains list of async tasks</returns>
        public MultipleMqlReadServiceAsyncResponse ReadMultipleAsync(params dynamic[] queries)
        {
            MultipleMqlReadServiceAsyncResponse response = new MultipleMqlReadServiceAsyncResponse();

            foreach (var query in queries)
            {
                Task<MqlReadServiceResponse> task = ReadInternalAsync(query);
                response.Results.Add(task);
            }
            return response;
        }

        /// <summary>
        /// Run all passed queries in async, wait until they are ready, then returns list of results
        /// </summary>
        /// <param name="queries">Array of queries that must be executed in async way</param>
        /// <returns></returns>
        public List<MqlReadServiceResponse> ReadMultipleAsyncWithWait(params dynamic[] queries)
        {
            List<MqlReadServiceResponse> response = new List<MqlReadServiceResponse>();
            List<Task<MqlReadServiceResponse>> tasks = new List<Task<MqlReadServiceResponse>>();

            foreach (var query in queries)
            {
                Task<MqlReadServiceResponse> task = ReadInternalAsync(query);
                tasks.Add(task);
            }
            Task.WaitAll(tasks.ToArray());

            response.AddRange(tasks.Select(x => x.Result));

            return response;
        }

        /// <summary>
        /// Run all passed queries consequently and returns list of results
        /// </summary>
        /// <param name="queries">Array of queries that must be executed consequently</param>
        /// <returns></returns>
        public MultipleMqlReadServiceResponse ReadMultiple(params dynamic[] queries)
        {
            MultipleMqlReadServiceResponse response = new MultipleMqlReadServiceResponse();

            foreach (var query in queries)
            {
                response.Results.Add(ReadInternal(query));
            }
            return response;
        }

        protected override MqlReadServiceResponse CreateResponse(RestResponseMessage response)
        {
            var freebaseResponse = new MqlReadServiceResponse
            {
                ResultAsString = response.Content,
                Status = response.StatusCode
            };

            if (freebaseResponse.Status == System.Net.HttpStatusCode.OK)
            {
                JObject content = JObject.Parse(response.Content);
                freebaseResponse.ResultAsObject = content;
                string cursor = (string)content["cursor"];
                if (!string.IsNullOrEmpty(cursor))
                {
                    freebaseResponse.Cursor = cursor;
                    freebaseResponse.HasCursor = true;
                }
                var resultList = (JArray)content["result"];
                foreach (var item in resultList)
                {
                    freebaseResponse.Results.Add((dynamic)item);
                }
            }

            return freebaseResponse;
        }

        protected override string _baseUrl
        {
            get { return Constants.MqlReadServiceUrl; }
        }

        protected override RestClient CreateRequest(dynamic query)
        {
            var parseToMql = new MqlParser(query);
            RestClient client = new RestClient { BaseUrl = _baseUrl };

            client.AddParameter("query", parseToMql.JsonString);

            if (!string.IsNullOrEmpty(_apiKey))
            {
                client.AddParameter("key", _apiKey);
            }

            client.RequestFormat = DataFormat.Json;
            return client;
        }

        public async Task<MqlReadServiceResponse> ReadWithDescrAndImageAsync(dynamic query)
        {
            var textService = FreebaseServices.CreateTextService();
            var imageService = FreebaseServices.CreateImageService();

            MqlReadServiceResponse result = await ReadInternalAsync(query);
            List<Task<TextServiceResponse>> tasks = new List<Task<TextServiceResponse>>();
            foreach (var item in result.Results)
            {
                //JObject temp = (JObject)item;
                if (!string.IsNullOrEmpty((string)item.id))
                {
                    tasks.Add(textService.ReadAsync(item.id));
                    item.ImageUrl = imageService.GetImageUrl((string)item.id);
                }
            }

            Task.WaitAll(tasks.ToArray());

            for (int i = 0; i < tasks.Count; i++)
            {
                result.Results[i].Description = tasks[i].Result.Result;
            }
            return result;
        }

        public MqlReadServiceResponse Read(dynamic query, int limit = 10, Cursor cursor = null, string asOfTime = "", string callback = "", bool htmlEscape = false, int indent = -1, string language = "/lang/en", bool cost = false, UniquenessFailure uniqueness_failure = UniquenessFailure.Hard)
        {
            query.limit = limit;
            return ReadInternal(query, GetParameters(cursor, asOfTime, callback, htmlEscape, indent, language, cost, uniqueness_failure));
        }

        public async Task<MqlReadServiceResponse> ReadAsync(dynamic query, int limit = 10, Cursor cursor = null, string asOfTime = "", string callback = "", bool htmlEscape = false, int indent = -1, string language = "/lang/en", bool cost = false, UniquenessFailure uniqueness_failure = UniquenessFailure.Hard)
        {
            query.limit = limit;

            return await ReadInternalAsync(query, GetParameters(cursor, asOfTime, callback, htmlEscape, indent, language, cost, uniqueness_failure));
        }

        private string[] GetParameters(Cursor cursor, string asOfTime, string callback, bool htmlEscape, int indent, string langauge, bool cost, UniquenessFailure uniqueness_failure)
        {
            List<string> parameters = new List<string>();
            if (cursor != null)
            {
                string cur = string.Empty;
                if (cursor.Use)
                {
                    cur = "cursor,";
                }
                if (!string.IsNullOrEmpty(cursor.Value))
                {
                    cur += cursor.Value;
                }
                parameters.Add(cur);
            }

            if (!string.IsNullOrEmpty(asOfTime))
                parameters.Add(string.Format("as_of_time,{0}", asOfTime));
            if (!string.IsNullOrEmpty(callback))
                parameters.Add(string.Format("callback,{0}", callback));
            if (htmlEscape)
                parameters.Add(string.Format("html_escape,{0}", htmlEscape.ToString().ToLower()));
            if (indent != -1)
                parameters.Add(string.Format("indent,{0}", indent));
            if (!string.IsNullOrEmpty(langauge))
                parameters.Add(string.Format("lang,{0}", langauge));
            if (cost)
                parameters.Add(string.Format("cost,{0}", cost.ToString().ToLower()));
            if (uniqueness_failure != UniquenessFailure.Hard)
                parameters.Add(string.Format("uniqueness_failure,{0}", ServicesHelpers.GetUniquenessFailure(uniqueness_failure)));

            return parameters.ToArray();
        }

        public string language { get; set; }
    }
}
